<?php

declare(strict_types=1);
/**
 * This file is part of MineAdmin.
 *
 * @link     https://www.mineadmin.com
 * @document https://doc.mineadmin.com
 * @contact  root@imoi.cn
 * @license  https://github.com/mineadmin/MineAdmin/blob/master/LICENSE
 */
use App\Setting\Service\SettingConfigService;
use App\System\Service\SystemUserService;
use App\System\Vo\AmqpQueueVo;
use App\System\Vo\QueueMessageVo;
use Carbon\Carbon;
use Hyperf\Cache\Listener\DeleteListenerEvent;
use Hyperf\Context\ApplicationContext;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Database\Model\Builder;
use Hyperf\Server\ServerFactory;
use Hyperf\Snowflake\IdGeneratorInterface;
use Mine\Interfaces\ServiceInterface\QueueLogServiceInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use function Hyperf\Collection\collect;

if (! function_exists('env')) {
    /**
     * 获取环境变量信息.
     */
    function env(string $key, mixed $default = null): mixed
    {
        return \Hyperf\Support\env($key, $default);
    }
}

if (! function_exists('config')) {
    /**
     * 获取配置信息.
     */
    function config(string $key, mixed $default = null): mixed
    {
        return \Hyperf\Config\config($key, $default);
    }
}

if (! function_exists('make')) {
    /**
     * Create an object instance, if the DI container exist in ApplicationContext,
     * then the object will be created by DI container via `make()` method, if not,
     * the object will create by `new` keyword.
     */
    function make(string $name, array $parameters = [])
    {
        return \Hyperf\Support\make($name, $parameters);
    }
}

if (! function_exists('sys_config')) {
    /**
     * 获取后台系统配置.
     *
     * @throws RedisException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function sys_config(string $key, mixed $default = null): mixed
    {
        return container()->get(SettingConfigService::class)->getConfigByKey($key) ?? $default;
    }
}

if (! function_exists('sys_group_config')) {
    /**
     * 获取后台系统配置.
     *
     * @param null|mixed $default
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function sys_group_config(string $groupKey, mixed $default = []): mixed
    {
        return container()->get(SettingConfigService::class)->getConfigByGroupKey($groupKey) ?? $default;
    }
}

if (! function_exists('push_queue_message')) {
    /**
     * 推送消息到队列.
     * @throws Throwable
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function push_queue_message(QueueMessageVo $message, array $receiveUsers = []): bool
    {
        return container()
            ->get(QueueLogServiceInterface::class)
            ->pushMessage($message, $receiveUsers);
    }
}

if (! function_exists('add_queue')) {
    /**
     * 添加任务到队列.
     * @throws Throwable
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function add_queue(AmqpQueueVo $amqpQueueVo): bool
    {
        return container()
            ->get(QueueLogServiceInterface::class)
            ->addQueue($amqpQueueVo);
    }
}

if (! function_exists('delete_cache')) {
    /**
     * 删除框架自带的注解缓存.
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function delete_cache(string $prefix, array $args): void
    {
        ApplicationContext::getContainer()
            ->get(EventDispatcherInterface::class)
            ->dispatch(new DeleteListenerEvent(
                $prefix,
                $args
            ));
    }
}

if (! function_exists('is_in_container')) {
    function is_in_container(): bool
    {
        if (file_exists('/.dockerenv')) {
            return true;
        }
        $cgroup = file_get_contents('/proc/1/cgroup');
        return strpos($cgroup, 'kubepods') > 0 || strpos($cgroup, 'docker') > 0;
    }
}

if (! function_exists('has_permission')) {
    /**
     * 检查用户是否拥有某权限.
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function has_permission(string $code, ?int $userId = null): bool
    {
        $codes = container()->get(SystemUserService::class)->getInfo($userId)['codes'];
        if ($codes[0] === '*') {
            return true;
        }
        return in_array($code, $codes);
    }
}

if (! function_exists('has_role')) {
    /**
     * 检查用户是否拥有某角色.
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function has_role(string $code, ?int $userId = null): bool
    {
        $roles = container()->get(SystemUserService::class)->getInfo($userId)['roles'];
        if ($roles === 'superAdmin') {
            return true;
        }
        return in_array($code, $roles);
    }
}


if (! function_exists('getSecurityKey')) {
    /**
     * 获取当前登录用户实例
     * @param string $scene
     * @return string
     */
    function getSecurityKey(string $scene = \App\Common\Constants\JwtAuthSceneConstants::CUST_HUB_URL): string
    {
        $loginUser = new \App\CustHub\Helpers\ChLoginUser($scene);
        $userInfo = $loginUser->getUserInfo();
        return $userInfo['security_key'] ?? '';
    }
}

if (!function_exists('auth_code')) {
    /**
     * 加密、解密函数
     * @param ?string $string 字符串，明文或密文
     * @param string $operation DECODE表示解密，ENCODE其它表示加密
     * @param string $key 密匙
     * @param int $expiry 密文有效期
     * @return bool|string
     */
    function auth_code($string, $operation = 'DECODE', $key = 'wikeyun', $expiry = 0)
    {
        // 动态密匙长度，相同的明文会生成不同密文就是依靠动态密匙
        $ckey_length = 4;

        // 密匙
        $key = md5($key ? $key : $GLOBALS['discuz_auth_key']);

        // 密匙a会参与加解密
        $keya = md5(substr($key, 0, 16));
        // 密匙b会用来做数据完整性验证
        $keyb = md5(substr($key, 16, 16));
        // 密匙c用于变化生成的密文
        $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) :
            substr(md5(microtime()), -$ckey_length)) : '';
        // 参与运算的密匙
        $cryptkey = $keya . md5($keya . $keyc);
        $key_length = strlen($cryptkey);
        // 明文，前10位用来保存时间戳，解密时验证数据有效性，10到26位用来保存$keyb(密匙b)，
        //解密时会通过这个密匙验证数据完整性
        // 如果是解码的话，会从第$ckey_length位开始，因为密文前$ckey_length位保存 动态密匙，以保证解密正确
        $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) :
            sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
        $string_length = strlen($string);
        $result = '';
        $box = range(0, 255);
        $rndkey = array();
        // 产生密匙簿
        for ($i = 0; $i <= 255; $i++) {
            $rndkey[$i] = ord($cryptkey[$i % $key_length]);
        }
        // 用固定的算法，打乱密匙簿，增加随机性，好像很复杂，实际上对并不会增加密文的强度
        for ($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $rndkey[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }
        // 核心加解密部分
        for ($a = $j = $i = 0; $i < $string_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            // 从密匙簿得出密匙进行异或，再转成字符
            $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }
        if ($operation == 'DECODE') {
            // 验证数据有效性，请看未加密明文的格式
            if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) &&
                substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
                return substr($result, 26);
            } else {
                return '';
            }
        } else {
            // 把动态密匙保存在密文里，这也是为什么同样的明文，生产不同密文后能解密的原因
            // 因为加密后的密文可能是一些特殊字符，复制过程可能会丢失，所以用base64编码
            return $keyc . str_replace('=', '', base64_encode($result));
        }
    }
}

if (! function_exists('chUser')) {
    /**
     * 获取当前登录用户实例.
     */
    function chUser(): \App\CustHub\Helpers\ChLoginUser
    {
        return new \App\CustHub\Helpers\ChLoginUser();
    }
}

if (! function_exists('chatUser')) {
    /**
     * 获取当前登录用户实例.
     */
    function chatUser(): \App\Chat\Helpers\ChatLoginUser
    {
        return new \App\Chat\Helpers\ChatLoginUser();
    }
}

if (!function_exists('make_coupon_card')) {
    /**
     * 随机邀请码
     * @param int $number 邀请码位数
     */
    function make_coupon_card($number = 6): string
    {
        $code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $rand = $code[rand(0, 25)]
            . strtoupper(dechex((int)date('m')))
            . date('d') . substr((string)time(), -5)
            . substr(microtime(), 2, 5)
            . sprintf('%02d', rand(0, 99));
        for (
            $a = md5($rand, true),
            $s = '0123456789ABCDEFGHIJKLMNOPQRSTUV',
            $d = '',
            $f = 0;
            $f < $number;
            $g = ord($a[$f]),
            $d .= $s[($g ^ ord($a[$f + 8])) - $g & 0x1F],
            $f++
        ) ;
        return $d;
    }
}

if (!function_exists('opt_arr')) {
    /**
     * 格式化数组，null值处理成空字符串、键值转换成字符串
     * @param array 数组，暂不支持object
     * @author cheng
     */
    function opt_arr($arr)
    {
        if (!is_array($arr) || empty($arr)) {
            return [];
        }
        foreach ($arr as $k => $v) {
            if (!is_array($v)) {
                if ($v === null) {
                    $arr[$k] = '';
                } else {
                    $arr[$k] = strval($v);
                }
            } else {
                $arr[$k] = opt_arr($v);
            }
        }
        return $arr;
    }
}

/**
 * 判断是否为空 过滤0的情况
 */
if(!function_exists('not_empty')){
    function not_empty($arg){
        if(!empty($arg) || is_numeric($arg)){
            return true;
        }
        return false;
    }
}


if(!function_exists('arrayToTree')){
    function arrayToTree(array $data = [], int|string $parentId = 0, string $id = 'id', string $parentField = 'parent_id', string $children = 'children'): array
    {
        if (empty($data)) {
            return [];
        }

        $tree = [];

        foreach ($data as $value) {
            if ($value[$parentField] == $parentId) {
                $child = arrayToTree($data, $value[$id], $id, $parentField, $children);
                if (! empty($child)) {
                    $value[$children] = $child;
                }
                array_push($tree, $value);
            }
        }

        unset($data);
        return $tree;
    }
}

if(!function_exists('matchTags')){

    /**
     * 判断标签是否满足条件
     *
     * @param array $conditions   条件数组
     * @param array $compareArray 对比数组
     * @param int   $type         类型: 0-任意一个标签满足 1-所有标签都满足
     *
     * @return bool
     *
     * @throws InvalidArgumentException
     */
    function matchTags(array $conditions, array $compareArray, int $type): bool
    {
        // 计算交集
        $intersection = array_intersect($conditions, $compareArray);

        switch ($type) {
            case 0:
                // 类型2: 满足任意一个标签 (交集大小大于0)
                return count($intersection) > 0;
            case 1:
                // 类型1: 所有标签都满足 (交集大小等于条件数组大小)
                return count($intersection) === count($conditions);
            default:
                //未知类型 返回false
                return false;
        }
    }
}


if (!function_exists('get_client_ip')) {
    /**
     * 获取客户端IP地址
     * @return mixed
     */
    function get_client_ip(){
        $isLocal = env('IS_LOCAL',false);
        if($isLocal){
            return '180.109.92.55';
        }
        $request = container()->get(\Mine\MineRequest::class);
        $headers = $request->getHeaders();
        if (isset($headers['x-forwarded-for'])) {
            //多层代理的场景
            $ip = $headers['x-forwarded-for'][0];
            $arr = explode(',',$ip);
            $pos    =   array_search('unknown',$arr);
            if(false !== $pos) unset($arr[$pos]);
            $ip     =   trim($arr[0]);
        }elseif (isset($headers['x-real-ip'])) {
            //单层代理的场景
            $ip = $headers['x-real-ip'][0];
        }elseif (isset($headers['http_x_forwarded_for'])) {
            $ip = $headers['http_x_forwarded_for'][0];
            $arr = explode(',',$ip);
            $pos    =   array_search('unknown',$arr);
            if(false !== $pos) unset($arr[$pos]);
            $ip     =   trim($arr[0]);
        }else  {
            $ip = $request->getServerParams()['remote_addr'] ?? '0.0.0.0';
        }

        return $ip;
    }

}


if(!function_exists('get_sleep_time')){

    function get_sleep_time(string $startTime,string $endTime)
    {
        $startTime = strtotime(date('Y-m-d '.$startTime));
        $endTime = strtotime(date('Y-m-d '.$endTime));
        if($startTime > $endTime){
            $endTime = \Carbon\Carbon::now()->timestamp($endTime)->timestamp;
        }
        return [$startTime,$endTime];
    }
}

if(!function_exists('datetime')){
    /**
     * 日期格式化
     * @param int|null $timestamp
     * @param string $format
     * @return string
     */
    function datetime(?int $timestamp,string $format = 'Y-m-d H:i:s'): string
    {
        return date($format,$timestamp);
    }
}

if(!function_exists('randomDate')){
    function randomDate($begin,$end,$format = "H:i:s") {
        $timestamp = rand($begin, $end);
        return date($format, $timestamp);
    }
}


if(!function_exists('generateOrderNumber')){
    /**
     * 生成订单号
     * @param string $prefix 前缀
     * @return string
     */
    function generateOrderNumber($prefix = ''): string
    {
        $generator = container()->get(IdGeneratorInterface::class);
        $orderNo = $prefix . date('YmdH') . $generator->generate();
        return $orderNo;
    }
}


if(!function_exists('randomFloat')){

    /**
     * 生成随机浮点数
     * @param int $min
     * @param int $max
     * @return float
     */
    function randomFloat(int $min = 0, int $max = 10, int $precision = 2): float
    {
        $num = $min + (mt_rand(1, mt_getrandmax() - 1) / mt_getrandmax()) * ($max - $min);

        return (float) sprintf('%.' . $precision . 'f', $num);
    }
}

if(!function_exists('isJson')){
   function isJson($value)
    {
        if (is_array($value)) {
            return false;
        }

        if (! is_scalar($value) && ! is_null($value) && ! method_exists($value, '__toString')) {
            return false;
        }

        json_decode($value);

        return json_last_error() === JSON_ERROR_NONE;
    }
}

if(!function_exists('get_remote_file_name')){
    /**
     * 获取远程文件名称
     * @param string $url
     * @return string
     */
    function get_remote_file_name(string $url): string
    {
        $parsedUrl = parse_url($url);
        if (isset($parsedUrl['path'])) {
            return basename($parsedUrl['path']);
        }

        return '';
    }
}


if(!function_exists('get_image_info')){
    /**
     * @param string $url
     * @return int[]
     */
    function get_image_info(string $url): array
    {
        $result = [
            'width' => 0,
            'height' => 0,
            'size' => 0
        ];

        // 验证 URL 是否有效
        if (!filter_var($url, FILTER_VALIDATE_URL)) {
            return $result;
        }

        // 获取图片的宽度、高度
        $imageInfo = @getimagesize($url);
        if ($imageInfo) {
            $result['width'] = $imageInfo[0];
            $result['height'] = $imageInfo[1];
        }

        // 使用 fopen 获取图片体积大小
        $handle = @fopen($url, 'rb');
        if ($handle) {
            $meta = stream_get_meta_data($handle);

            // 解析文件大小，兼容 nginx 和 apache
            $dataInfo = $meta['wrapper_data'] ?? [];
            foreach ($dataInfo as $header) {
                if (preg_match('/Content-Length:\s*(\d+)/i', $header, $matches)) {
                    $result['size'] = (int)$matches[1];
                    break;
                }
            }

            // 关闭文件流
            fclose($handle);
        }

        return $result;
    }
}

if(!function_exists('roomIsForbidChangeName')){
    /**
     * 是否禁止修改群名称
     * @param int $roomIsForbidChangeName
     * @param int $customerRoomType
     * @return int
     */
    function roomIsForbidChangeName(int $roomIsForbidChangeName,int $customerRoomType): int
    {
        $isForbid = 0;
        switch ($customerRoomType){
            case 0:
                //企微群
                break;
            case 1:
                //外部微信群
                if($roomIsForbidChangeName == 2097157){
                    $isForbid = 1;
                }
                break;
            case 2:
                //客户群
                if($roomIsForbidChangeName == 67108871 || $roomIsForbidChangeName == 7){
                    $isForbid = 1;
                }
                break;
        }
        return $isForbid;
    }

    if(!function_exists('applyFilters')){
        /**
         * 根据条件过滤数据
         * @param Builder $query
         * @param $params
         * @param array $filters
         * @return Builder
         */
        function applyFilters(Builder $query, $params, array $filters): Builder
        {
            foreach ($filters as $field => $operator) {
                if (!array_key_exists($field, $params) || $params[$field] === '' || $params[$field] === null) {
                    continue; // 跳过未提供的字段或空值
                }
                $value = $params[$field];

                if ($field === 'region') {
                    // 处理 region 字段的查询
                    if ($operator === 'like') {
                        $value = "%{$value}%";
                        $query->where(function ($subQuery) use ($value) {
                            $subQuery->where('province', 'like', $value)
                                ->orWhere('city', 'like', $value);
                        });
                    }
                    continue;
                }
                // 处理 LIKE 查询，添加 `%`
                if ($operator === 'like') {
                    $value = "%{$value}%";
                }

                // 处理 IN 查询，确保 $value 是数组
                if ($operator === 'in' && is_array($value) && !empty($value)) {
                    $query->whereIn($field, $value);
                } else {
                    $query->where($field, $operator, $value);
                }
            }
            return $query;
        }
    }
}

if (!function_exists('get_next_send_time')){

    /**
     * 获取下次一次发送时间
     * @param string $pushTime 推送时间 09:00
     * @param int $unit 0=每天  1每周 2 按月
     * @param array $val
     * @return string
     */
    function get_next_send_time(string $pushTime, int $unit, array $val): string
    {
        $now = Carbon::now();
        $hour = (int)substr($pushTime, 0, 2); // 小时
        $minute = (int)substr($pushTime, 3, 2); // 分钟

        switch ($unit) {
            case 0: // 每天
                $executionTime = Carbon::createFromTime($hour, $minute, 0, $now->timezone);

                if ($now->greaterThanOrEqualTo($executionTime)) {
                    $executionTime = $executionTime->addDay();
                }
                break;

            case 1: // 每周
                $daysOfWeek = $val; // 直接使用 val 数组中的值，0 表示周日

                $executionTime = null;
                foreach ($daysOfWeek as $dayOfWeek) {
                    // 将 7 转换为 0（周日）
                    $isoDayOfWeek = ($dayOfWeek === 7) ? 0 : $dayOfWeek;

                    $tempTime = Carbon::createFromTime($hour, $minute, 0, $now->timezone)
                        ->setDaysFromStartOfWeek((int)$isoDayOfWeek);

                    if ($tempTime->greaterThanOrEqualTo($now)) {
                        $executionTime = $tempTime;
                        break;
                    }
                }

                if ($executionTime === null) {
                    // 如果当前时间已经过了本周的所有指定时间，则设置为下一周的第一个指定时间
                    $isoDayOfWeek = $daysOfWeek[0] === 7 ? 0 : $daysOfWeek[0];
                    $executionTime = Carbon::createFromTime($hour, $minute, 0, $now->timezone)
                        ->setDaysFromStartOfWeek((int)$isoDayOfWeek)
                        ->addWeek();
                }
                break;
            case 2: // 每月
                $executionTime = null;
                foreach ($val as $dayOfMonth) {
                    $tempTime = Carbon::create($now->year, $now->month, $dayOfMonth, $hour, $minute, 0, $now->timezone);

                    if ($tempTime->greaterThanOrEqualTo($now)) {
                        $executionTime = $tempTime;
                        break;
                    }
                }

                if ($executionTime === null) {
                    // 如果当前时间已经过了本月的所有指定时间，则设置为下个月的第一个指定时间
                    $executionTime = Carbon::create($now->year, $now->month, $val[0], $hour, $minute, 0, $now->timezone)
                        ->addMonth();
                }
                break;

            default:
                throw new \InvalidArgumentException("Invalid unit provided: $unit");
        }

        return $executionTime->toDateTimeString();
    }
}


if(!function_exists('calculatePercentages')){

    /**
     * 计算百分比
     * @param array $data
     * @return array
     */
    function calculatePercentages(array $data): array {
        $totalValue = array_sum($data);
        if ($totalValue == 0) {
            $result = [];
            foreach ($data as $key => $value) {
                $result[$key] = ['value' => 0, 'percent' => '0'];
            }
            return $result;
        }

        $precision = 10000; // 精度调整值
        $result = [];
        $integerParts = [];
        $fractionalParts = [];
        $currentSum = 0;

        foreach ($data as $key => $value) {
            $percentage = ($value / $totalValue) * $precision;
            $integerPart = floor($percentage);
            $fractionalPart = $percentage - $integerPart;

            $integerParts[$key] = $integerPart;
            $fractionalParts[$key] = $fractionalPart;
            $currentSum += $integerPart;
        }

        // 调整小数部分
        while ($currentSum < $precision) {
            $maxIndex = array_search(max($fractionalParts), $fractionalParts);
            $integerParts[$maxIndex]++;
            $fractionalParts[$maxIndex] = 0;
            $currentSum++;
        }

        // 构建结果数组
        foreach ($integerParts as $key => $value) {
            $percent = \App\Common\Helpers\BcMath::round($value / $precision * 100,2);
            $result[$key] = [
                'value' => $data[$key],
                'percent' => $percent
            ];
        }
        return $result;
    }

}


if(!function_exists('toKeyArray'))
{
    /**
     * @param $array
     * @param $field
     * @return array
     */
    function toKeyArray($array,$field): array
    {
        return  array_column($array, null,$field);
    }
}


if(!function_exists('is_sleep_needed'))
{
    /**
     * 判断是否需要休息
     * @param mixed $sleep_period_switch
     * @param array $sleep_period_cfg
     * @return bool
     */
    function is_sleep_needed(mixed $sleep_period_switch,array $sleep_period_cfg): bool
    {
        $sleepPeriodSwitch = $sleep_period_switch;
        $needToSleep = false;
        if($sleepPeriodSwitch){
            $sleepPeriodCfg = $sleep_period_cfg;
            foreach ($sleepPeriodCfg as $config){
                //休眠时间段
                list($sleepStartTime,$sleepEndTime) = get_sleep_time($config['start_time'],$config['end_time']);

                if($sleepPeriodSwitch && $sleepStartTime <= time() && time() <= $sleepEndTime){
                    $needToSleep = true;
                    break;
                }
            }
        }
        return $needToSleep;
    }
}


if(!function_exists('uniqueArrayByField')){
    /**
     * 根据指定字段对二维数组进行去重
     *
     * @param array $array 原始二维数组
     * @param string $field 要去重的字段名
     * @return array 去重后的二维数组
     */
    function uniqueArrayByField(array $array, string $field): array
    {
        // 提取指定字段的值
        $values = array_column($array, $field);

        // 使用 array_unique 去重
        $uniqueValues = array_unique($values);

        // 生成新的去重后的数组
        $uniqueArray = [];
        foreach ($uniqueValues as $value) {
            foreach ($array as $item) {
                if ($item[$field] === $value) {
                    $uniqueArray[] = $item;
                    break;
                }
            }
        }

        return $uniqueArray;
    }
}



if(!function_exists('getContactIds')){
    /**
     * 获取客户数组
     */
    function getContactIds(array $array): array
    {
       $robotIds=[];
       $contacts=[];
       foreach ($array as $item){
           $robotIds[]=$item['robot_id'];
           $contacts=array_merge($contacts,$item['wxids']);
       }
        $contacts=array_unique($contacts);
       return compact('robotIds','contacts');
    }
}

if(!function_exists('checkKeywords')){

    /**
     * 检查关键词
     * @param array $keywords
     * @param string $content
     * @param int $type 0=模糊匹配,1=精确匹配
     * @param int $matchType 0=满足一个,1=满足所有
     * @return bool
     */
    function checkKeywords(array $keywords,string $content, int $type = 0,int $matchType = 0): bool
    {
        if($type == 0){
            //模糊匹配
            $matches = collect($keywords)->filter(function ($keyword) use ($content){
                return str_contains($content,$keyword);
            })->count();

            if($matchType == 0){
                //满足一个
                return $matches > 0;
            }else{
                //满足所有
                return $matches == count($keywords);
            }
        }else{
            //精确匹配
            $matches = collect($keywords)->filter(function ($keyword) use ($content){
                return $keyword == $content;
            })->count();

            return $matches > 0;
        }
    }
}

if(!function_exists('getRandomEmoji')){

    /**
     * 获取随机表情
     * @param int $number
     * @return string
     */
    function getRandomEmoji(int $number = 5): string
    {
        $arr = ['[愉快]','[得意]','[呲牙]','[憨笑]','[嘿哈]','[调皮]','[拥抱]','[笑脸]','[太阳]','[爱心]','[玫瑰]','[礼物]','[咖啡]'];

        $emoji = collect($arr)->random($number)->implode('');
        return $emoji;
    }
}
if(!function_exists('getDatesOfMonth')){
    /**
     * @param $year
     * @return array
     */
    function getDatesOfMonth($year, $month) {
        // 确保月份在有效范围内
        if ($month < 1 || $month > 12) {
            return [];
        }
        $carbonDate = Carbon::create($year, $month);
        // 获取该月的天数
        $daysInMonth = $carbonDate->daysInMonth;

        $datesArray = [];

        // 构建日期数组
        for ($day = 1; $day <= $daysInMonth; $day++) {
            $datesArray[] = sprintf('%04d-%02d-%02d', $year, $month, $day);
        }

        return $datesArray;
    }
}


if(!function_exists('applyFilters')){
    /**
     * 根据条件过滤数据
     * @param Builder $query
     * @param $params
     * @param array $filters
     * @return Builder
     */
    function applyFilters(Builder $query, $params, array $filters): Builder
    {
        foreach ($filters as $field => $operator) {
            if (!array_key_exists($field, $params) || $params[$field] === '' || $params[$field] === null) {
                continue; // 跳过未提供的字段或空值
            }
            $value = $params[$field];

            if ($field === 'region') {
                // 处理 region 字段的查询
                if ($operator === 'like') {
                    $value = "%{$value}%";
                    $query->where(function ($subQuery) use ($value) {
                        $subQuery->where('province', 'like', $value)
                            ->orWhere('city', 'like', $value);
                    });
                }
                continue;
            }
            // 处理 LIKE 查询，添加 `%`
            if ($operator === 'like') {
                $value = "%{$value}%";
            }

            // 处理 IN 查询，确保 $value 是数组
            if ($operator === 'in' && is_array($value) && !empty($value)) {
                $query->whereIn($field, $value);
            } else {
                $query->where($field, $operator, $value);
            }
        }
        return $query;
    }
}

if(!function_exists('di')){
    function di(): \Psr\Container\ContainerInterface
    {
        return container();
    }
}

if(!function_exists('stdout_log')){
    function stdout_log()
    {
        return di()->get(StdoutLoggerInterface::class);
    }
}

if(!function_exists('push_redis_subscribe')){
    /**
     * 推送消息到 Redis 订阅中
     *
     * @param string       $chan
     * @param string|array $message
     */
    function push_redis_subscribe(string $chan, $message)
    {
        $rst = redis()->publish($chan, is_string($message) ? $message : json_encode($message));
        stdout_log()->info("推送消息到 Redis 订阅中：" . ($rst ? '成功' : '失败'));
    }
}

if(!function_exists('server')) {
    function server()
    {
        return di()->get(ServerFactory::class)->getServer()->getServer();
    }
}

if (! function_exists('hyperf_url')) {
    function hyperf_url(
        string $routeName,
        array $params = [],
        ?string $domain = null
    ): string {
        // 参数编码处理
        $encodedParams = array_map(fn($v) => rawurlencode((string)$v), $params);

        // 替换路径参数
        $path = preg_replace_callback('/\{(\w+)\}/', function ($m) use ($encodedParams) {
            return $encodedParams[$m[1]] ?? '';
        }, $routeName);

        return  rtrim($domain, '/') . '/'.ltrim($path, '/');
    }
}


if (!function_exists('vendor')) {

    /**
     * 快速导入第三方框架类库 所有第三方框架的类库文件统一放到 系统的Vendor目录下面
     * @param string $class 类库
     * @param string $baseUrl 基础目录
     * @param string $ext 类库后缀
     * @return boolean
     */
    function vendor($class, $baseUrl = '', $ext='.php') {
        if (empty($baseUrl))
            $baseUrl = BASE_PATH."/extend/";
        return import($class, $baseUrl, $ext);
    }
}

if (!function_exists('import')) {

    /**
     * 导入所需的类库 同java的Import 本函数有缓存功能
     * @param string $class 类库命名空间字符串
     * @param string $baseUrl 起始路径
     * @param string $ext 导入的文件扩展名
     * @return boolean
     */
    function import($class, $baseUrl = '', $ext='.php') {
        static $_file = array();
        $class = str_replace(array('.', '#'), array('/', '.'), $class);

        if (isset($_file[$class . $baseUrl])){
            return true;
        }else{
            $_file[$class . $baseUrl] = true;
        }
        $class_strut     = explode('/', $class);

        if (substr($baseUrl, -1) != '/')
            $baseUrl    .= '/';
        $classfile       = $baseUrl . $class . $ext;

        if (!class_exists(basename($class),false)) {
            // 如果类不存在 则导入类库文件
            return require_cache($classfile);
        }
        return null;

    }
}

/*------------------兼容老版本-----------------------------*/
if (!function_exists('require_cache')) {
    /**
     * 优化的require_once
     * @param string $filename 文件地址
     * @return boolean
     */
    function require_cache($filename) {
        static $_importFiles = array();
        if (!isset($_importFiles[$filename])) {
            if (file_exists_case($filename)) {
                require $filename;
                $_importFiles[$filename] = true;
            } else {
                $_importFiles[$filename] = false;
            }
        }
        return $_importFiles[$filename];
    }
}


if (!function_exists('file_exists_case')) {

    /**
     * 区分大小写的文件存在判断
     * @param string $filename 文件地址
     * @return boolean
     */
    function file_exists_case($filename) {
        if (is_file($filename)) {

            return true;
        }
        return false;
    }
}

if(!function_exists('objtoarr')){
    function objtoarr($obj)
    {
        $ret = array();
        foreach ($obj as $key => $value) {
            if (gettype($value) == 'array' || gettype($value) == 'object') {
                $ret[$key] = objtoarr($value);
            } else {
                $ret[$key] = $value;
            }
        }
        return $ret;
    }
}

if(!function_exists('getBeString')){
    function getBeString($a,$b,$str){
        $start = mb_strpos($str,$a) + mb_strlen($a);
        $end = mb_strrpos($str,$b) - $start;
        //    return trim(mb_substr($str,$start,$end));
        return mb_substr($str,$start,$end);
    }
}

if(!function_exists('smartDiffTimePrecise')){

    /**
     * 智能计算两个时间戳的差值，精确到秒
     * @param $date1
     * @param $date2
     * @return string
     */
    function smartDiffTimePrecise($date1, $date2): string
    {
        $carbon1 = Carbon::parse($date1);
        $carbon2 = Carbon::parse($date2);

        $seconds = $carbon1->diffInSeconds($carbon2);

        if ($seconds < 60) {
            return $seconds . '秒';
        }

        $minutes = $carbon1->diffInMinutes($carbon2);
        if ($minutes < 60) {
            // 显示带小数的分钟（如1.5分钟）
            return round($seconds / 60, 1) . '分钟';
        }

        $hours = $carbon1->diffInHours($carbon2);
        if ($hours < 24) {
            // 显示带小数的小时（如1.5小时）
            return round($seconds / 3600, 1) . '小时';
        }

        $days = $carbon1->diffInDays($carbon2);
        return $days . '天';
    }
}

if (!function_exists('convert_url_query')) {
    function convert_url_query($query)
    {
        $queryParts = array_filter(explode('&', $query));
        $params = array();
        foreach ($queryParts as $param) {
            $item = explode('=', $param);
            $params[$item[0]] = $item[1];
        }
        return $params;
    }
}

if(!function_exists('create_qrcode_url')){
    function create_qrcode_url($url): string
    {
        return "https://router.wikeyun.cn/qr/?text=".$url;
    }
}

if(!function_exists('stdout_log')){
    /**
     * 控制台日志
     *
     * @return StdoutLoggerInterface
     */
    function stdout_log():StdoutLoggerInterface
    {
        return container()->get(StdoutLoggerInterface::class);
    }
}


if(!function_exists('decodeIfBase64')){
    function decodeIfBase64($input):string
    {
        // 1. 去除所有空白字符（包括空格、换行、制表符等）
        $cleaned = preg_replace('/\s+/', '', $input);

        // 2. 检查字符串是否为空
        if ($cleaned === '') {
            return $input; // 空字符串不是有效Base64
        }

        // 3. 检查字符集是否合法（仅允许Base64字符）
        if (preg_match('/^[a-zA-Z0-9\/+]+={0,2}$/', $cleaned) !== 1) {
            return $input; // 包含非法字符
        }

        if (strlen($cleaned) < 8 || strlen($cleaned) % 4 !== 0) {
            return $input;
        }

        // 4. 尝试解码（严格模式）
        $decoded = base64_decode($cleaned, true);
        if ($decoded === false) {
            return $input; // 解码失败
        }

        // 5. 验证解码后的内容（可选：避免误判纯文本）
        if (base64_encode($decoded) !== $cleaned) {
            return $input; // 编码后不匹配（处理填充问题）
        }

        return $decoded;
    }
}


if(!function_exists('camelToUnder')){
    /**
     * 驼峰转下划线
     * @param $input
     * @return array|string
     */
    function camelToUnder($input):array|string
    {
        // 如果是数组，递归处理每个元素
        if (is_array($input)) {
            $result = [];
            foreach ($input as $key => $value) {
                // 转换键名
                $newKey = is_string($key) ? \Hyperf\Stringable\Str::snake($key) : $key;
                // 递归处理值
                $result[$newKey] = is_array($value) ? camelToUnder($value) : $value;
            }
            return $result;
        }

        // 如果是字符串，直接转换
        if (is_string($input)) {
            return \Hyperf\Stringable\Str::snake($input);
        }
        return $input;
    }
}

if(!function_exists('underToCamel')){
    /**
     * 下划线转驼峰
     * @param $input
     * @return array|string
     */
    function underToCamel($input):array|string
    {
        // 如果是数组，递归处理每个元素
        if (is_array($input)) {
            $result = [];
            foreach ($input as $key => $value) {
                // 转换键名
                $newKey = is_string($key) ? \Hyperf\Stringable\Str::camel($key) : $key;
                // 递归处理值
                $result[$newKey] = is_array($value) ? underToCamel($value) : $value;
            }
            return $result;
        }

        // 如果是字符串，直接转换
        if (is_string($input)) {
            return \Hyperf\Stringable\Str::camel($input);
        }
        return $input;
    }
}


if (!function_exists('last')) {
    /**
     * 获取数组最后一个元素
     * @param array $array
     * @return mixed|null
     */
    function last(array $array)
    {
        return empty($array) ? null : end($array);
    }
}